package com.lianqi.emcpframework.ddd.gencode.application;

import javax.lang.model.element.Modifier;

import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import com.lianqi.emcpframework.ddd.gencode.support.TypeBuilderFactory;
import com.lianqi.emcpframework.design.domain.CreateAuditDataSupplier;
import com.lianqi.emcpframework.design.domain.UpdateAuditDataSupplier;
import com.lianqi.emcpframework.design.domain.event.DomainEventBus;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

public final class ApplicationSupportBuilderFactory implements TypeBuilderFactory {
    private final String className;
    private final boolean genIfc;
    private final String superClassName;
    private final String ifcFullName;
    // private final TypeElement modelType;
    // private final TypeElement repositoryType;
    private final boolean createDefaultElement;

    public ApplicationSupportBuilderFactory(String className, boolean genIfc, String superClassName, String ifcFullName,
        boolean createDefaultElement) {
        this.className = className;
        this.genIfc = genIfc;
        this.superClassName = superClassName;
        this.ifcFullName = ifcFullName;
        this.createDefaultElement = createDefaultElement;
    }

    @Override
    public TypeSpec.Builder create() {
        TypeSpec.Builder builder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
            .superclass(ClassName.bestGuess(superClassName));
        if (this.genIfc) {
            builder.addSuperinterface(ClassName.bestGuess(ifcFullName));
        }
        if (createDefaultElement) {
            writeDefaultElements(builder);
            writeAuditDataSupplier(builder);
        }
        return builder;
    }

    private void writeDefaultElements(TypeSpec.Builder applicationImplBuilder) {
        MethodSpec.Builder cMethodSpecBuilder =
            MethodSpec.constructorBuilder().addParameter(ClassName.get(Logger.class), "logger")
                .addStatement("super(logger)").addModifiers(Modifier.PROTECTED);
        applicationImplBuilder.addMethod(cMethodSpecBuilder.build());

        MethodSpec.Builder emptyMethodSpecBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PROTECTED);
        applicationImplBuilder.addMethod(emptyMethodSpecBuilder.build());

        applicationImplBuilder.addField(FieldSpec.builder(ClassName.get(DomainEventBus.class), "domainEventBus")
            .addModifiers(Modifier.PRIVATE).addAnnotation(Autowired.class).build());

        MethodSpec.Builder eventBusGetter =
            MethodSpec.methodBuilder("getDomainEventBus").addModifiers(Modifier.PROTECTED)
                .returns(ClassName.get(DomainEventBus.class)).addStatement("return this.domainEventBus");

        applicationImplBuilder.addMethod(eventBusGetter.build());

    }

    /**
     * 生成创建 、修改相关数据 提供者方法
     * 
     * @param applicationImplBuilder
     */
    private void writeAuditDataSupplier(TypeSpec.Builder applicationImplBuilder) {

        applicationImplBuilder
            .addField(FieldSpec.builder(ClassName.get(CreateAuditDataSupplier.class), "createAuditData")
                .addModifiers(Modifier.PRIVATE).addAnnotation(Autowired.class).build());

        MethodSpec.Builder createAuditDataGetter =
            MethodSpec.methodBuilder("getCreateAuditDataSupplier").addModifiers(Modifier.PROTECTED)
                .returns(ClassName.get(CreateAuditDataSupplier.class)).addStatement("return this.createAuditData");

        applicationImplBuilder.addMethod(createAuditDataGetter.build());

        applicationImplBuilder
            .addField(FieldSpec.builder(ClassName.get(UpdateAuditDataSupplier.class), "updateAuditData")
                .addModifiers(Modifier.PRIVATE).addAnnotation(Autowired.class).build());

        MethodSpec.Builder updateAuditDataGetter =
            MethodSpec.methodBuilder("getUpdateAuditDataSupplier").addModifiers(Modifier.PROTECTED)
                .returns(ClassName.get(UpdateAuditDataSupplier.class)).addStatement("return this.updateAuditData");

        applicationImplBuilder.addMethod(updateAuditDataGetter.build());

    }
}
